#pragma once

#include "mp3types.h"
#include <resource/Resource.h>

// max. 1730 bytes per frame: 144 * 384kbit/s / 32000 Hz + 2 Bytes CRC
const uint32 BufferIntSize = 433;

enum e_syncmode { INITIAL_SYNC, STRICT_SYNC };

class CBitStream
{
	resource::Resource * res;

	uint32 buffer[BufferIntSize];
	uint32 *wordpointer;	// position of next unsigned int for get_bits()

	uint32 framesize;		// number of valid bytes in buffer
	uint32 bitindex;		// number (0-31, from MSB to LSB) of next bit for get_bits()
	uint32 syncword;
	bool single_ch_mode;
	int32 current_frame_number;

public:
	explicit CBitStream( resource::Resource * res )
	:	res( res ),
		bitindex(0),
		current_frame_number(-1),
		wordpointer(buffer)
	{};
	~CBitStream()
	{
		delete res; res = 0;
	};

	// get next 32 bits from bitstream in an unsigned int, returned value False => end of stream
	bool get_header(uint32 *headerstring,enum e_syncmode syncmode);

	// fill buffer with data from bitstream, returned value False => end of stream
	bool read_frame(uint32 bytesize);

	// read bits (1 <= number_of_bits <= 16) from buffer into the lower bits
	// of an unsigned int. The LSB contains the latest read bit of the stream
	uint32 get_bits(uint32 number_of_bits);

	// Set the word we want to sync the header to, in Big-Endian byte order
	void set_syncword(uint32 syncword0);

	int32 current_frame() const { return current_frame_number; }

	// Returns the size, in bytes, of the input file
	uint32 file_size() const { return res->get_size(); };
};

#ifdef DAMN_INTEL_BYTE_ORDER
#define swap_int32(int_32) (((int_32) << 24) | (((int_32) << 8) & 0x00ff0000) | (((int_32) >> 8) & 0x0000ff00) | ((int_32) >> 24))
#endif

inline bool CBitStream::read_frame(uint32 bytesize)
{
	// read bytesize bytes from the file, placing the number of bytes
	// actually read in numread and setting result to true if successful
	int32 numread = res->read(buffer,bytesize);

	wordpointer = buffer;
	bitindex    = 0;
	framesize   = bytesize;

#ifdef DAMN_INTEL_BYTE_ORDER
	uint32 *wordp, word;
	for (wordp = buffer + ((numread - 1) >> 2); wordp >= buffer; --wordp)
	{
		word = *wordp;
		*wordp = swap_int32(word);
	}
#endif
#pragma warning (disable:4018)
  return (numread == (int32)framesize);
};

inline void CBitStream::set_syncword(uint32 syncword0)
{
#ifdef DAMN_INTEL_BYTE_ORDER
	syncword = swap_int32(syncword0 & 0xFFFFFF3F);
#else
	syncword = syncword0 & 0xFFFFFF3F;
#endif

	single_ch_mode = ((syncword0 & 0x000000C0) == 0x000000C0);
};
